#include <iostream>

using namespace std;

// 1、const 修饰普通变量
void test1(){
	
	// 必须初始化，不可再赋新值
	const int a = 10;
	int const b = 30;// 均可
	
	const int v[] = {1, 2, 3, 4};
	//error: const int a2;	未被初始化
	
	//error: b = 99;		不可再更改
	//error: v[2] = 99;		不可再修改
	cout << "b = " << b << endl;
	
	// 用指针指向常量，该指针必须为 常量指针const*
	int const* p = &b;
	//error: int * const p2 = &b; 指针常量不可指向 常量
	//error: int* p2 = &a;
	//error: int * const p2 = &a;
	cout << "*p = " << *p << endl;
	
	
	// 普通指针、指针常量、常量指针，均可指向变量
	float c = 3;
	float* p2 = &c;
	float* const p3 = &c; 
	const float* p4 = &c;
}


// 2、const修饰指针
void test2() {
	// 2.1 const修饰p
	int i = 5;
	int j = 85;
	int * const p = &i;
	cout << "i = " << i << endl;
	
	//p不可改变指向，但可以改变*p
	//error: p = &j;
	*p = 45;
	cout << "i = " << i << endl;
	
	// 2.2 const修饰*p2
	int const *p2 = &i;
	cout << "*p2 = " << *p2 << endl;
	//*p2不可改，p2可改
	//error: *p2 = 65;
	p2 = &j;
	cout << "*p2 = " << *p2 << endl;
	
	
	//2.3 const修饰*p3,p3
	const int * const p3 = &i;
	//*p3, p3均不可改
	cout << "p3 = " << p3 << endl;
	cout << "*p3 = " << *p3 << endl;
	//error: p3 = &j;
	//error: *p3 = 86;

	cout << "p3 = " << p3 << endl;
	cout << "*p3 = " << *p3 << endl;

}


// 3、const修饰数组
void test3(){
	// 数组变量实际上就是const的指针，所以不能直接赋值
	int a[5]; // int * const a;
	int b[5] = {1, 2, 3, 4, 5};
	// error: a = b;
	
	
	//const修饰数组表明数组的每个元素都是const int，无法修改，所以必须通过初始化进行赋值
	//error: const int c[5];
	const int c[5] = {1,2,3,4,5};
}


//4、const修饰函数形参
//4.1 const修饰普通形参变量
//func1内不可修改形参a的值，也无法影响到实参
void func1(const int a){}

//4.2 const修饰指针形参
//保证*p不会改变，保证实参安全，但p会改变
void func2(const int *p){
	//*p = 2; 	Illegal，*p不可改变
	int i = *p; // OK 
	//int* p2 = p; 	 Illegal，常量指针不可赋值给普通指针 -- 防止指针p2改内存数据
}

// 可以改变*p，无法保证实参安全
void func3(int * const p){}

//4.3 const修饰引用形参
//传递的是外部实参本身，无需进行拷贝，增加了效率
//const引用，无法通过引用修改实参，保证了外部数据的安全性。
void func4(const int &a){}

/*
注意：
	如果函数参数是非const的引用/指针，它就不能接收const变量（地址），因为不能保证函数内部对实参进行修改，会造成权限的放大，会报错，它只能接收非const变量（地址）

	而如果函数参数是const的引用/指针，它既可以接收const变量（地址），也可以接收非const变量（地址）
	
	如果函数是const普通变量，那么它可以接收const变量，也可以接收非const变量，因为不会造成权限的放大。
*/


//5、const 修饰函数返回值
//5.1 const修饰普通类型的返回值
//这个实际上是毫无意义的，因为返回的实际上是临时变量，临时变量本身就具有常性
const int fun1(){return 0;};

//5.2 const修饰指针类型的返回值
const int* fun2(){int a = 0;return &a;}
const int* p1 = fun2();

//因为fun2()实际上是一个const int*指针，我们无法通过指针修改其所指变量，
//而把fun2()这个const int*指针赋值给p2后，p2是非const指针，可以通过指针修改其所指变量，这就造成了指针所指变量的访问权限的放大
//error: int* p2 = fun2();

int* const fun3(){int a = 0; return &a;}
int* p3 = fun3();
//我们可以把fun3()看作一个int* const指针，尽管该指针不可以修改，但是我们可以通过该指针修改其所指变量
//赋值给p3后，我们依然可以通过指针修改其所指变量，对指针所指变量的访问权限没有发生变化



class A{
public:
	//6、const修饰成员变量
	const int money;//成员常量，不能被修改，同时它只能在初始化列表中赋值 或者 直接在此行中赋值
	int age;
	mutable double height;//不再是类内部的状态，可以被const成员函数修改
	
	A():money(100){
	}
	
	//7、const修饰成员函数
	//this指针的本质：A* const this;即不可改变this的指向，但可以改变*this，即改变成员变量
	//在const成员函数内，this前有const修饰，const this不可在修改成员变量，其本质变成 const A* const p;
	void show() const
	{
		//error: age = 18;
		height = 3.1415;
		cout << "age = " << age << endl;
	}
	
	void print()
	{
		cout << "age = " << age << endl;
		cout << "money = " << money << endl;
	}
};


//8、const修饰类对象
void test8()
{
	const A a;
	//不可在改变内部成员变量，除非成员变量用mutable修饰
	//error: a.age = 100;
	a.height = 3.14;
	
	//只可调用const成员函数，保证成员变量不会在成员函数内进行修改
	a.show();
	//error: a.print();
}

int main(int argc, char *argv[]) {
	//test1();
	
	//test2();
	
	test8();
}